import numpy as np
import matplotlib.pylab as plt
import pandas as pd
import seaborn as sns
import statsmodels.api as sm
from scipy.stats import shapiro
from scipy.stats import kstest
from scipy import stats

# 用来显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 用来正常显示负号
plt.rcParams['axes.unicode_minus'] = False


def normfun(x, mu, sigma):
    """
    正态分布的概率密度函数
    :param x: 数据集中的某一具体测量值
    :param mu: 数据集的平均值，反映测量值分布的集中趋势
    :param sigma: 数据集的标准差，反映测量值分布的分散程度
    :return:
    """
    pdf = np.exp(-((x - mu) ** 2) / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi))
    return pdf


# 画出来的图最好调一下柱子宽度、数量等参数以获得最好的效果
def draw(name, data):
    # PP图和QQ图
    sm.qqplot(data, line='s')
    # 计算均值
    mean = np.mean(data)
    # 计算方差
    std = np.std(data)
    # z-score标准化方法(数据标准化)
    data1 = [(x - mean) / std for x in data]
    # 直方图柱子的数量
    num_bins = 40
    # plt.figure(figsize=(20, 8))
    plt.figure()
    # data数据  num_bins柱子个数 range取值范围[-3.3] rwidth柱子宽度
    # n, bins, patches = plt.hist(data1, num_bins, range=[-5, 5], rwidth=0.8, density=0.9, facecolor='blue', alpha=0.5)
    n, bins, patches = plt.hist(data1, num_bins, rwidth=3, density=2, facecolor='blue')

    # 直方图函数，x为x轴的值，density=1表示为概率密度，即和为一，绿色方块，色深参数0.5.返回n个概率，直方块左边线的x值，及各个方块对象
    # print(bins)
    # 拟合一条最佳正态分布曲线（方程）y  代替品 ——>>> from scipy.stats import norm  y = norm.pdf(bins, mu, sigma)
    y = normfun(bins, np.mean(data1), np.std(data1))
    plt.plot(bins, y, 'r--')  # 绘制y的曲线
    plt.xlabel('sepal-length')  # 绘制x轴
    plt.ylabel('Probability')  # 绘制y轴
    plt.title(r'{}  $\mu={}$,$\sigma={}$'.format(name, np.round(mean, 7), np.round(std, 7)))  # 标题
    plt.subplots_adjust(left=0.15)  # 左边距
    # out_file = 'out_pic/%s.png' % name
    # plt.savefig(out_file, transparent=True, bbox_inches='tight', dpi=200, pad_inches=0.0, set_visiable=False,
    #            format='png')
    # plt.show()


# def gradient（list_of_colors）：
#     width = 600
#     height = 480
#     img = Image.new
#     draw = ImageDraw.Draw（img）
# 对于范围内的i（len（list_of_colors））：
# r1，g1，b1 = list_of_colors [i] 对于x在范围（width / len（list_of_colors））：
# color =（r1，g1，b1）
# draw.line（（x +（width / len（list_of_colors）* i），0，x + （width / len（list_of_colors）* i），height），fill = color）
# img.show（）
# gradient（[（30，198，244） （99,200,72），（120,50,80），（200,90,140）]）

def draw2(data):
    plt.figure()
    # 绘制频率直方图，20个箱体
    plt.hist(data, bins=20)
    # 绘制带正态曲线的频率直方图
    sns.distplot(data)

    # sm.ProbPlot


def Normal_Inspection(data):
    # 参考
    # https://www.jianshu.com/p/41be87e8d57e
    # 1、夏皮洛-威尔克检验（Shapiro—Wilk test）,一般又称W检验,W检验是一种类似于利用秩进行相关性检验的方法.
    # 同样需要注意的是，W检验与K-S检验一样，原假设是“样本数据来自的分布与正态分布无显著差异”，因此一般来说，W检验最终返回两个结果，分别是检验统计量及P值。
    # 检验结果P>0.05才是我们的目标
    # 当数据集中的数据无重复值时，该方法的检验效果比较好，但是当数据集中有些数据不是独一无二的，即有些数据的数值是相同的，那么该方法的检验效果就不是很好
    w, p_w = shapiro(data)
    # 2、柯尔莫戈洛夫-斯米诺夫检验（Kolmogorov-Smirnov test），一般又称K-S检验
    # 是一种基于累计分布函数的非参数检验，用以检验两个经验分布是否不同或一个经验分布与另一个理想分布是否不同
    # K-S检验的原假设是“样本数据来自的分布与正态分布无显著差异”，因此一般来说，KS检验最终返回两个结果，分别是检验统计量及P值
    # 检验结果P>0.05才是我们的目标
    kt, p_kt = kstest(data, cdf="norm")
    # normaltest中pvalue>0.05符合正态分布

    # 3、D'Agostino and Pearson omnibus normality test
    # GraphPad官方推荐使用该方法
    # 首先计算 偏度和峰度以便在不对称和形状方面量化分布离高斯分布的距离。
    # 然后，其计算这些值中的每一个与高斯分布的预期值之间的差异，并基于这些差异的总和，计算各P值。
    # 这是一种通用和强大的正态性检验，推荐使用
    # 请注意，D'Agostino开发了几种正态性检验。Prism使用的其中一个是“综合K2”检验。
    # #normaltest中pvalue>0.05符合正态分布
    d, p_d = stats.normaltest(data)

    # 安德森-达令检验样本数据是否来自特定分布，包括分布：'norm', 'expon', 'gumbel', 'extreme1' or 'logistic'.
    # 原假设 H0：样本服从特定分布； 备择假设 H1：样本不服从特定分布
    # 用Anderson-Darling检验生成的数组是否服从正态分布
    # statistic:安德森-达林检验统计量
    # critical_values:此分布的临界值
    # significance_level:相应临界值的显著性级别(以百分比表示)。该函数根据测试所依据的分布，返回一组不同重要性级别的临界值
    statistic, critical_values, significance_level = stats.anderson(data, dist='norm')
    '''输出AndersonResult(statistic=0.18097695613924714, 
                          critical_values=array([ 0.555,  0.632,  0.759,  0.885,  1.053]), 
                          significance_level=array([ 15. ,  10. ,   5. ,   2.5,   1. ]))
    如果输出的统计量值statistic < critical_values，则表示在相应的significance_level下，
    接受原假设，认为样本数据来自给定的正态分布。'''
    # print(critical_values)
    # print(significance_level)
    Result = pd.DataFrame([[w, kt, d, statistic], [p_w, p_kt, p_d, None]], index=['统计量', 'p值'],
                          columns=['W检验', 'K-S检验', 'D\'Agostino检验', '安德森-达令检验'])
    return Result
